package properties;


import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

import org.omg.CosNaming.IstringHelper;

import properties.Alldiff;
import properties.AdittionConstraint;
import properties.Carry;
import properties.Letter;
import parser.PuzzleParser;

import es.deusto.ingenieria.is.csp.algorithms.CSPAlgorithm;
import es.deusto.ingenieria.is.csp.formulation.CSPProblem;
import es.deusto.ingenieria.is.csp.formulation.Constraint;
import es.deusto.ingenieria.is.csp.formulation.Variable;

public class AdittionProblem extends CSPProblem<Integer>{//es integer{

	private ArrayList<ArrayList<Letter>> operands;
	private ArrayList<Letter> result;
	private int numerOperands;
	private int maximunLength;

	public AdittionProblem() {
		// TODO Auto-generated constructor stub
		this.createVariables();
		this.createConstraints();
	}


	private void createVariables() {
		// TODO Auto-generated method stub
		PuzzleParser puzzle = new PuzzleParser("data/additionpuzzle1.xml");
		result = puzzle.getResult();
		operands = puzzle.getOperands();
		numerOperands = puzzle.getNumOperands();
		maximunLength = puzzle.getMaxLength();
		ArrayList<Integer> values = new ArrayList<Integer>(); 
		ArrayList<Letter> letters = new ArrayList<Letter>();
		//We create a list with all the posible values for the carry
		for(int i=0;i<numerOperands;i++){
			values.add(i);
		}
		//We introduce the carry variables
		for(int i=0;i<=maximunLength;i++){
			this.addVariable(new Carry(Integer.toString(i), values));
		}
		boolean enc = false;
		//We introduce the result letters
		for(int i=0;i<result.size();i++){
			enc=false;
			for(int j=0;j<letters.size();j++){
				//We search for the same word in the variables
				if(result.get(i).getName().equals(letters.get(j).getName())){
					//If already exists we introduce the new values like part of the result
					letters.get(j).setPosition(i);
					letters.get(j).isResult();
					enc = true;
				}
			}
			if(!enc){
				//If it didn't exist we introduce to the list like part of the result
				result.get(i).setPosition(i);
				result.get(i).isResult();
				letters.add(result.get(i));
			}
		}
		//We introduce the operands
		for(int i=0;i<operands.size();i++){
			//We start with the first position of the operands 
			int position = result.size()-maximunLength;
			enc=false;
			for(int j=0;j<operands.get(i).size();j++){
				//We search for the letter
				for(int p=0;p<letters.size();p++){
					if(operands.get(i).get(j).getName().equals(letters.get(p).getName())){
						//If already exists we introduce the new values 
						letters.get(p).setPosition(position);
						letters.get(p).noResult();
						enc = true;
						break;
					}
				}
				if(!enc){
					//If it didn't exist we introduce to the list
					operands.get(i).get(j).setPosition(position);
					operands.get(i).get(j).noResult();
					letters.add(operands.get(i).get(j));
				}
				position++;
			}
		}
		//We introduce all the variables in the problem
		for(int i=0;i<letters.size();i++){
			this.addVariable(letters.get(i));
		}
		
		
	}


	private void createConstraints() {
		// TODO Auto-generated method stub
		ArrayList<Variable<Integer>> variables = new ArrayList<Variable<Integer>>();
		//We select only the letters
		for (Variable<Integer> variable : this.getVariables()) {
			if(variable instanceof Letter){
				variables.add(variable);
			}
		}
		//We add the allDiff constraint 
		Alldiff allDiff = new Alldiff("AllDif",variables);
		for(int i = 0;i<variables.size();i++){
			variables.get(i).addConstraint(allDiff);
		}
		//For each of the columns we add a sum constraint
		for(int i = 0;i<this.result.size();i++){
			variables = new ArrayList<Variable<Integer>>();
			//We search the variables of that column and we add it to the list
			for (Variable<Integer> variable : this.getVariables()) {
				if(variable instanceof Carry){
					if(Integer.parseInt(((Carry)variable).getName())==i ||Integer.parseInt(((Carry)variable).getName())==i+1){
						variables.add(variable);
					}
				}else{
					if(((Letter)variable).havePosition(i)!=-1){
						variables.add(variable);
					}
				}
			}
			//We add to the list of that column the sum constraint
			AdittionConstraint sum = new AdittionConstraint(Integer.toString(i),variables);
			for(int j=0;j<variables.size();j++){
				variables.get(j).addConstraint(sum);
			}
		}
	}


	public ArrayList<ArrayList<Letter>> getOperands() {
		return operands;
	}


	public void setOperands(ArrayList<ArrayList<Letter>> operands) {
		this.operands = operands;
	}


	public ArrayList<Letter> getResult() {
		return result;
	}


	public void setResult(ArrayList<Letter> result) {
		this.result = result;
	}


	public int getNumOperands() {
		return numerOperands;
	}


	public void setNumOperands(int numOperands) {
		this.numerOperands = numOperands;
	}


	public int getMaxLength() {
		return maximunLength;
	}


	public void setMaxLength(int maxLength) {
		this.maximunLength = maxLength;
	}

	public void solve(CSPAlgorithm<Integer> algorithm) {
		SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss.S");
		Date beginDate = GregorianCalendar.getInstance().getTime();
		System.out.println("\n* Start '" + algorithm.getClass().getSimpleName() + "' (" + formatter.format(beginDate) + ")");				
		boolean solutionFound = algorithm.solve(this);
		Date endDate = GregorianCalendar.getInstance().getTime();		
		System.out.println("* End   '" + algorithm.getClass().getSimpleName() + "' (" + formatter.format(endDate) + ")");
		
		long miliseconds = (int) Math.abs(beginDate.getTime() - endDate.getTime());
		long seconds = miliseconds / 1000;
		miliseconds %= 1000;		
		long minutes = seconds / 60;
		seconds %= 60;
		long hours = minutes / 60;
		minutes %= 60;
		
		String time = "\n* Serach lasts: ";
		time += (hours > 0) ? hours + " h " : " ";
		time += (minutes > 0) ? minutes + " m " : " ";
		time += (seconds > 0) ? seconds + "s " : " ";
		time += (miliseconds > 0) ? miliseconds + "ms " : " ";
		
		System.out.println(time);
		
		if (solutionFound) {
			System.out.println("\n- :-) Solution found!");			
			System.out.println("\n" + this);
		} else {
			System.out.println("\n- :-( Unable to find the solution!");
		}
	}


}
